/**
	Constructs a SoundController singelton to controll sound in a movie.

	@author Matthew Richmond
	@version 2005-09-15
	@Revised 2005-02-15 Thiago deMello Bueno 
	@usage

	- Activate with:
	<code>
	SoundController.getInstance();
	SoundController.init(this); // pass the path to where you want the temp clips made
	</code>

	- Toggle power (sound) on/off:
	<code>
	SoundController.power(true);
	SoundController.power(false);
	</code>

	- Set volume of al sounds playing:
	<code>
	SoundController.volume(30);
	</code>

	- Play a sound (linkage id, [loop, volume]):
	<code>
	SoundController.play("blip1_id", 3, 50);
	SoundController.play("blip1_id", 3);
	SoundController.play("blip1_id");
	</code>
	
	- if you dont specify a volume the sound uses mainAudioVol
	- if yoy specify a volume the sound uses specified volume
	- if you adjust mainAudioVol while a soun dis playing it will shift that volume
*/
import timber.ui.ProgressIndicator;

class timber.sound.SoundController {

	private static var _instance = null;
	
	private var audioOn:Boolean = true;
	private var audioVol:Number = 100;
	private var playingSounds:Object;
	private var soundIndex:Number = 0;

	private var mc:MovieClip;
	private var obj:MovieClip;
	
	private var baseUrl:String = "";		// base url of any loaded audio files
	
	private var loadProgress:Boolean = false;
	private var progress_mc:ProgressIndicator;
	
	// ===========================================================
	// - CONSTRUCTOR
	// ===========================================================
	
	private function SoundController() {
		//trace("SoundController:");
		AsBroadcaster.initialize(this);
		playingSounds = new Object();
	};

	// ===========================================================
	// - GET SINGLETON INSTANCE
	// ===========================================================
	
	public static function getInstance() {
		//trace("SoundController: getInstance");
		if (_instance == null) {
			SoundController._instance = new SoundController();
		};
		return SoundController._instance;
	};

	// ===========================================================
	// - INIT
	// ===========================================================
	
	/**
	Initilize sound object
	
	@param	obj MovieClip location of where sound objects are stored
	*/
	public static function init(obj:MovieClip) {
		//trace("SoundController: init");
		var sc = SoundController.getInstance();
		
		// store a reference to the obj where we make a mess of clips
		sc.obj = obj;
		
		create ();
	}

	// ===========================================================
	// - CREATE
	// ===========================================================

	public static function create (){
		//trace("SoundController: create");
		var sc = SoundController.getInstance();
		
		// store depth for hold clip
		//sc.depth = sc.obj.getNextHighestDepth();
		// create a movie clip to store sound objects in (lame but only way to control volume independantly)
		sc.mc = sc.obj.createEmptyMovieClip("sc_mc", 1);
	};

	// ===========================================================
	// - ACESSORS
	// ===========================================================
	public static function setBaseUrl (val:String) {
		var sc = SoundController.getInstance();
		
		sc.baseUrl = val;
	};

	public static function useLoadProgress (val:Boolean){
		//trace("SoundController: useLoadProgress");
		var sc = SoundController.getInstance();
		
		sc.loadProgress = val;
	};


	/**
		Returns the mc used for a progress indicator

		@return MovieClip
	*/
	public static function getProgressClip() {
		var sc = SoundController.getInstance();
		
		return sc.progress_mc;
	};
	
	/**
		Sets the clip to use for a progress indicator.

		@param  val new ProgressIndicator clip
	*/
	public static function setProgressClip(val:ProgressIndicator) {
		var sc = SoundController.getInstance();
		
		useLoadProgress (true);
		sc.progress_mc = val;
	};
	
	
	// ===========================================================
	// - POWER
	// ===========================================================
	public static function power(choice:Boolean) {
		//trace("SoundController: power");
		var sc = SoundController.getInstance();
				
		if ( (choice == true) && (sc.audioOn != true) ) {
			//trace("SoundController: power: switching on");
			
			// set the on/off var
			sc.audioOn = choice;
			
			create ();
			
		} else if ( (choice == false) && (sc.audioOn != false) ) {
			//trace("SoundController: power: switching off");
			
			// set the on/off var
			sc.audioOn = choice;
			
			// turn everything off
			for (var x in sc.playingSounds){
				sc.playingSounds[x].stop();
				delete sc.playingSounds[x];
			}
			
			// delete movie clip that stores sound objects
			removeMovieClip(sc.obj.sc_mc);
		};
		
	};
	
	/**
	setVolume takes one parameter a number 0-100, which scales the volumes of all playing sounds to that percentage of their current volume.
	
	@param  Param Name Param Desrption
	*/
	public static function setVolume(vol:Number) {
		trace("SoundController: setVolume to "+vol);
		var sc = SoundController.getInstance();
		//var prevAudioVol = sc.audioVol;
		
		//trace("--- you requested "+vol);
		
		// set volume of all sounds currently playing
		for (var x in sc.playingSounds){
			var currentsound:Object = sc.playingSounds[x];
			
			var prevCurrentVol:Number = currentsound.getVolume();
			//trace("audioVol was "+ sc.audioVol + ", sound"+x+" was " + prevCurrentVol);
			
			// doing the rounding keeps the levels from creeping, bah...
			var percent:Number = Math.ceil( (currentsound.getVolume() / sc.audioVol) * 100 );
			//trace("percent is: "+percent );
			currentsound.setVolume(vol * (percent * .01 ) );	
			
			var currentVol:Number = currentsound.getVolume();
			//trace("audioVol is "+vol + ", sound"+x+" is " + currentVol);
		};
		
		// set new master volume
		sc.audioVol = vol;
	};
		
	public function calcEventVolume (v:Number):Number{
		//trace("SoundController: calcEventVolume: sent "+v);
		
		//trace("Current audioVol is "+audioVol);
		
		var val:Number;
		// condition ? trueExpression : falseExpression
		if (!v) {
			//trace("SoundController: calcEventVolume: No percentage requested!");
			val = audioVol;
		} else {
			//trace("SoundController: calcEventVolume: "+v+" of "+audioVol+" requested!");
			val = audioVol/100 * v;
		}
		
		return val;
	};
	
	/*
	I'm not entirely sure why thiago made two of these, i think it has to do with the soundfader clip. After further investigation i'm not entirely sure if the associated methods below, or the SoundFader Class is required. I have reverted to the one setVolume method.
	*/
	
	/*public static function setAbsVolume(vol:Number) {
		var sc = SoundController.getInstance();
		
		trace("setAbsVolume: "+vol);
		
		sc.audioVol = vol;
		// set volume of all sounds currently playing
		for (var x in sc.playingSounds){
			var currentsound:Object = sc.playingSounds[x];
			var currentVol:Number = currentsound.getVolume();
			currentsound.setVolume(vol);
		}
	};
	
	public static function setVolume(vol:Number) {
		var sc = SoundController.getInstance();
		sc.audioVol = vol;
		// set volume of all sounds currently playing
		for (var x in sc.playingSounds){
			var currentsound:Object = sc.playingSounds[x];
			var currentVol:Number = currentsound.getVolume();
			var newvol:Number = currentVol * vol/100
			currentsound.setVolume(newvol);
		}
	};*/

	public static function play(id:String, loop:Number, percentVol:Number, callback:Function) {
		//trace("SoundController: play: "+id);
		var sc = SoundController.getInstance();

		if (sc.audioOn == true) {
			
			//up the count
			var currentIndex = sc.soundIndex++;
			
			// make a new movie clip so we can have independant volume
			var current:MovieClip = sc.mc.createEmptyMovieClip(id+"_mc", sc.mc.getNextHighestDepth());
			
			// make new sound object
			var my_sound:Sound = new Sound(current);
			
			// did the user specify a volume			
			var my_vol:Number = sc.calcEventVolume(percentVol);
			
			my_sound.setVolume(my_vol);
			
			my_sound.attachSound(id);

			// push the new sound into our object
			sc.playingSounds[currentIndex] = my_sound;
			
			// tell the sound to call the kill me method on complete
			my_sound.onSoundComplete = function (){
				SoundController.killSound(currentIndex, callback);
				delete this;
			};
			
			// did the user specify a loop count? and play!
			if (loop == undefined) {
				my_sound.start(0,1);
			} else {
				my_sound.start(0,loop);
			}
		
		} else {
			trace("SoundController: play: POWER IS OFF!");
		}
	};

	public static function load (path:String, loop:Number, percentVol:Number, callback:Function, streaming:Boolean, duration:Number){
		//trace("SoundController: load: "+path);
		var sc = SoundController.getInstance();
		
		if (sc.audioOn == true) {
			
			//up the count
			var currentIndex = sc.soundIndex++;
			
			//modify the path
			var n = path.substr(0, -4)
			
			// make a new movie clip so we can have independant volume
			var current:MovieClip = sc.mc.createEmptyMovieClip(n+"_mc", sc.mc.getNextHighestDepth());
			
			// make new sound object
			var my_sound:Sound = new Sound(current);
			
			// did the user specify a volume? with loaded sounds we have to set once loaded
			//var newvol:Number = (!percentVol)? 100 : sc.audioVol/100 * percentVol;
			//trace("SoundController: main volume is "+sc.audioVol + ", mine is " + newvol);
			
			var my_vol:Number = sc.calcEventVolume(percentVol);
			
			my_sound.onLoad = function(success:Boolean) {
				
				if (success) {
					//trace("SoundController: load: load success");
					
					if (!streaming) {
					
						// set sound volume
						my_sound.setVolume(my_vol);


						// if our id is still in the list of currently playing songs, we play
						if ( sc.isSoundInPlaying(currentIndex) ) {

							if (loop == undefined) {
								my_sound.start();
							} else {
								my_sound.start(0,loop);
							}

						};
						
					};
					
				} else {
					trace("SoundController: load: load failed");
				}
			}
			
			if(streaming){
				//trace("SoundController: load: streaming true");
				// set sound volume
				my_sound.setVolume(my_vol);
				
				// load and loop
				my_sound.loadSound(sc.baseUrl+path, true);
				
			}else{
			// load the sound, tell it not to stream
				my_sound.loadSound(sc.baseUrl+path, false);
			}
				
			// are we going to display loading
			if (sc.loadProgress) {
				
				// make a clip to hold the preload graphic
				var lc:MovieClip = sc.obj.createEmptyMovieClip( "loader"+currentIndex+"_mc", sc.obj.getNextHighestDepth() )
				
				onLoadStart(my_sound)
			
			
				if ( (streaming) && (duration) ) {
				
					//if (duration) {
						lc.duration = duration
					
						lc.onEnterFrame = function  (){

							var ratio = my_sound.position/this.duration
					
							var percentLoaded = Math.round(ratio*100);
							//trace("percentLoaded is: "+percentLoaded);
							if ( isNaN( percentLoaded ) ) percentLoaded = 0;

							onLoadProgress(my_sound, my_sound.position, this.duration );

							// this condition will check for your load and stop the process
							if ( percentLoaded >= 100 ) {
								onLoadComplete(my_sound)
								delete (this.onEnterFrame);
							};	
						};
					//};
				
				} else {
				
					lc.onEnterFrame = function  (){

						var ratio = my_sound.getBytesLoaded()/my_sound.getBytesTotal();

						var percentLoaded = Math.round(ratio*100);
						//trace("percentLoaded is: "+percentLoaded);
						if ( isNaN( percentLoaded ) ) percentLoaded = 0;

						onLoadProgress(my_sound, my_sound.getBytesLoaded(), my_sound.getBytesTotal() );

						// this condition will check for your load and stop the process
						if ( percentLoaded >= 100 ) {
							onLoadComplete(my_sound)
							delete (this.onEnterFrame);
						};	
					};
				
				};
			
			};
			
			// push the new sound into our object
			sc.playingSounds[currentIndex] = my_sound;
			
			// tell the sound to call the kill me method on complete
			my_sound.onSoundComplete = function (){
				
				if (sc.loadProgress) {
					getProgressClip().reset();
				};
						
				SoundController.killSound(currentIndex, callback);
				delete this;
			};
		
		} else {
				trace("SoundController: load: POWER IS OFF!");		
		};
		
	};

	public static function onLoadStart(clip) {
		getProgressClip().setValue(0);
		getProgressClip().startAnimation();
	}
	
	public static function onLoadProgress(clip, loadedBytes, totalBytes) {
		getProgressClip().setTotal(totalBytes);
		getProgressClip().setValue(loadedBytes);
		getProgressClip().update();
	}
	
	public static function onLoadComplete(clip) {
		// stop progress ani
		getProgressClip().stopAnimation();
	}

	public static function killSound (id:String, callback:Function){
		//traceAllSound ();
		//trace("SoundController: killSound: "+id+" callback: "+callback);
		var sc = SoundController.getInstance();
	
		sc.playingSounds[id].stop();
		delete sc.playingSounds[id];
		
		var cmc = sc.mc[id+"_mc"];
		removeMovieClip(cmc);
		
		//traceAllSound ();
		
		// if we have a callback, call it
		if (callback) {
			callback();
		}
	};
	
	public static function killAllSound (){
		//trace("SoundController: killAllSound");
		var sc = SoundController.getInstance();
		
		// stop all loading sounds
		if (sc.loadProgress) {
			for (var x in sc.playingSounds){
				//remove the preloader art
				var lmc = sc.obj["loader"+x+"_mc"];
				removeMovieClip (lmc);
			};
		};
		
		// turn sounds off
		for (var x in sc.playingSounds){
			sc.playingSounds[x].stop();
			delete sc.playingSounds[x];
		};

	};
	
	
	public static function traceAllSound (){
		//trace("SoundController: traceAllSound");
		var sc = SoundController.getInstance();
		
		var l:String = "SoundController: playing:"
		
		for (var x in sc.playingSounds){
			l += " "+ x +","
			//sc.playingSounds[x].stop();
			//delete sc.playingSounds[x];
		};
		
		trace(l);
	};
	
	
	public function isSoundInPlaying (val:Number):Boolean{
		//trace("SoundController: isSoundInPlaying");
		var sc = SoundController.getInstance();
		
		for (var x in sc.playingSounds){
			if (x == val) return true;
		};
		
		return false;
	};
	
	/*public static function getSound(id:String){
		var sc = SoundController.getInstance();
		var cs = sc.playingSounds[id];
		return cs;
	}
	
	public static function setAbsSoundVolume(id:String,vol:Number){
		var sound = SoundController.getSound(id);
		sound.setVolume(vol);
	}

	public static function setSoundVolume(id:String,vol:Number){
		var sc = SoundController.getInstance();
		var sound = sc.getSound(id);
		var newvol:Number = sc.audioVol/100 * sound.getVolume();
		sound.setVolume(newvol);
	}*/

};